﻿/*
/*
 * IconHandler - by Gil Schmidt
 *
 * - Originaly was writtin for a p2p project called Kibutz.
 * - The IconFromExtension might give you some problems if it won't find an
 *   icon for it( try/catch should be used).
 *
 * [Updated]
 *
 * - Added Shell function for getting file extension after getting some commet
 *   about it, check link below.
 *   (http://www.codeguru.com/Csharp/Csharp/cs_misc/icons/article.php/c4261/)
 * - IconFromResource was added for having all the common needed functions for
 *   handling icons.
 * - Readed DestroyIcon and adding GetManagedIcon, after verifying it's needed
 *   (thanks DrGui and Kenneth Broendum).
 * - Removed the IconFromExtension that pulled the icon from the registry
 *   (IconFromExtensionShell is default now).
 * - Fixed IconCount mistake (if the number of icon in the file was 0 and the
 *   index that was selected was also 0 the ExtractIconEx still got to run and
 *   crashed (thanks SickLab).
 * - Fixed iIcon (it was changed from IntPtr to int. on x64 systems IntPtr is
 *   8 bytes which caused the mistake to popup) parameter in SHFILEINFO struct
 *   (Thanks Stefan Mayr).
 *
 * contact me at: Gil_Smdt@Hotmail.com
 *
*/

using System;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;

namespace IconHandler
{
  internal struct SHFILEINFO
  {
    public IntPtr hIcon;
    public int iIcon;
    public uint dwAttributes;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
    public string szDisplayName;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
    public string szTypeName;
  };

  public enum IconSize : uint
  {
    Large = 0x0,  //32x32
    Small = 0x1 //16x16
  }

  //the function that will extract the icons from a file
  public class IconHandler
  {
    private const uint SHGFI_ICON = 0x100;
    private const uint SHGFI_USEFILEATTRIBUTES = 0x10;

    [DllImport("Shell32", CharSet = CharSet.Auto)]
    internal extern static int ExtractIconEx(
        [MarshalAs(UnmanagedType.LPTStr)]
            string lpszFile,                //size of the icon
        int nIconIndex,

      //index of the icon (in case we have more
      //than 1 icon in the file
        IntPtr[] phIconLarge,           //32x32 icon
        IntPtr[] phIconSmall,           //16x16 icon
        int nIcons);                    //how many to get

    [DllImport("shell32.dll")]
    private static extern IntPtr SHGetFileInfo(
        string pszPath,                //path
        uint dwFileAttributes,        //attributes
        ref SHFILEINFO psfi,        //struct pointer
        uint cbSizeFileInfo,        //size
        uint uFlags);    //flags

    //we need this function to release the unmanaged resource,
    //the unmanaged resource will be copies to a managed one and
    //it will be returned.
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern bool DestroyIcon(IntPtr handle);

    //will return an array of icons
    public static Icon[] IconsFromFile(string Filename, IconSize Size)
    {
      int IconCount = ExtractIconEx(Filename, -1, null, null, 0);

      //checks how many icons.
      IntPtr[] IconPtr = new IntPtr[IconCount];
      Icon TempIcon;

      //extracts the icons by the size that was selected.
      if (Size == IconSize.Small)
        ExtractIconEx(Filename, 0, null, IconPtr, IconCount);
      else
        ExtractIconEx(Filename, 0, IconPtr, null, IconCount);

      Icon[] IconList = new Icon[IconCount];

      //gets the icons in a list.
      for (int i = 0; i < IconCount; i++)
      {
        TempIcon = (Icon)Icon.FromHandle(IconPtr[i]);
        IconList[i] = GetManagedIcon(ref TempIcon);
      }

      return IconList;
    }

    //extract one selected by index icon from a file.
    public static Icon IconFromFile(
        string Filename, IconSize Size, int Index)
    {
      int IconCount = ExtractIconEx(Filename, -1, null, null, 0);

      //checks how many icons.
      if (IconCount <= 0 || Index >= IconCount) return null;

      // no icons were found.

      Icon TempIcon;
      IntPtr[] IconPtr = new IntPtr[1];

      //extracts the icon that we want in the selected size.
      if (Size == IconSize.Small)
        ExtractIconEx(Filename, Index, null, IconPtr, 1);
      else
        ExtractIconEx(Filename, Index, IconPtr, null, 1);

      TempIcon = Icon.FromHandle(IconPtr[0]);

      return GetManagedIcon(ref TempIcon);
    }

    public static Icon IconFromExtension(string Extension, IconSize Size)
    {
      try
      {
        Icon TempIcon;

        //add '.' if nessesry
        if (Extension[0] != '.') Extension = '.' + Extension;

        //temp struct for getting file shell info
        SHFILEINFO TempFileInfo = new SHFILEINFO();

        SHGetFileInfo(
            Extension,
            0,
            ref TempFileInfo,
            (uint)Marshal.SizeOf(TempFileInfo),
            SHGFI_ICON | SHGFI_USEFILEATTRIBUTES | (uint)Size);

        TempIcon = (Icon)Icon.FromHandle(TempFileInfo.hIcon);
        return GetManagedIcon(ref TempIcon);
      }
      catch (Exception e)
      {
        System.Diagnostics.Debug.WriteLine(
            "error while trying to get icon for " + Extension +
            " :" + e.Message);
        return null;
      }
    }

    /*
     * this is the built in funcion provided by .Net framework for getting
     * a file icon, i thought it's worth mentioning.
     *
    public static Icon IconFromExtension(string Filename)
    {
        return Icon.ExtractAssociatedIcon(Filename);
    }
    */

    public static Icon IconFromResource(string ResourceName)
    {
      Assembly TempAssembly = Assembly.GetCallingAssembly();

      return new Icon(TempAssembly.GetManifestResourceStream(
          ResourceName));
    }

    public static void SaveIconFromImage(
        Image SourceImage, string IconFilename, IconSize DestenationIconSize)
    {
      Size NewIconSize = DestenationIconSize ==
          IconSize.Large ? new Size(32, 32) : new Size(16, 16);

      Bitmap RawImage = new Bitmap(SourceImage, NewIconSize);
      Icon TempIcon = Icon.FromHandle(RawImage.GetHicon());
      FileStream NewIconStream = new FileStream(
          IconFilename, FileMode.Create);

      TempIcon.Save(NewIconStream);

      NewIconStream.Close();
    }

    public static void SaveIcon(Icon SourceIcon, string IconFilename)
    {
      FileStream NewIconStream = new FileStream(
          IconFilename, FileMode.Create);

      SourceIcon.Save(NewIconStream);

      NewIconStream.Close();
    }

    public static Icon GetManagedIcon(ref Icon UnmanagedIcon)
    {
      Icon ManagedIcon = (Icon)UnmanagedIcon.Clone();

      DestroyIcon(UnmanagedIcon.Handle);

      return ManagedIcon;
    }
  }
}

/* EOF */